#ifndef EXPRESSIONPREPROCESSED_H
#define EXPRESSIONPREPROCESSED_H

#include <vector>
#include <ostream>
#include <wx/string.h>

namespace Expression {
  enum executionError {
    executedSuccessfully,
    errorArgumentIndex,
    errorOperationIndex,
    errorStackIndex,
    errorContentIndex,
    errorOperation,
    errorDivByZero,
    errorType,
    errorScript,
  };
  class Value {
    public:
      Value( signed char v ) { SetSignedInt( v ); }
      Value( signed short v ) { SetSignedInt( v ); }
      Value( signed int v ) { SetSignedInt( v ); }
      Value( signed long v ) { SetSignedInt( v ); }
      Value( signed long long v ) { SetSignedInt( v ); }
      Value( unsigned char v ) { SetUnsignedInt( v ); }
      Value( unsigned short v ) { SetUnsignedInt( v ); }
      Value( unsigned int v ) { SetUnsignedInt( v ); }
      Value( unsigned long v ) { SetUnsignedInt( v ); }
      Value( unsigned long long v ) { SetUnsignedInt( v ); }
      Value( float v ) { SetFloat( v ); }
      Value( double v ) { SetFloat( v ); }
      Value( long double v ) { SetFloat( v ); }
      inline bool IsSignedInt() { return m_Type == tSignedInt; }
      inline bool IsUnsignedInt() { return m_Type == tUnsignedInt; }
      inline bool IsFloat() { return m_Type == tFloat; }
      inline signed long long GetSignedInt() { if( !IsSignedInt() ) throw errorType; return m_SignedInt; }
      inline unsigned long long GetUnsignedInt() { if( !IsUnsignedInt() ) throw errorType; return m_UnsignedInt; }
      inline long double GetFloat() { if( !IsFloat() ) throw errorType; return m_Float; }
      bool operator< ( const Value& second ) const;
      template< typename T >
      inline bool operator== ( T value ) {
        if( IsSignedInt() ) {
          return value == ( T )m_SignedInt;
        }
        if( IsUnsignedInt() ) {
          return value == ( T )m_UnsignedInt;
        }
        if( IsFloat() ) {
          return value == ( T )m_Float;
        }
        return false;
      }

      template< typename T >
      inline bool operator< ( T value ) {
        if( IsSignedInt() ) {
          return value > ( T )m_SignedInt;
        }
        if( IsUnsignedInt() ) {
          return value > ( T )m_UnsignedInt;
        }
        if( IsFloat() ) {
          return value > ( T )m_Float;
        }
        return false;
      }

      template< typename T >
      inline bool operator<= ( T value ) {
        if( IsSignedInt() ) {
          return value >= ( T )m_SignedInt;
        }
        if( IsUnsignedInt() ) {
          return value >= ( T )m_UnsignedInt;
        }
        if( IsFloat() ) {
          return value >= ( T )m_Float;
        }
        return false;
      }

      template< typename T >
      inline bool operator> ( T value ) {
        return !operator<= ( value );
      }

      template< typename T >
      inline bool operator>= ( T value ) {
        return !operator< ( value );
      }

      friend inline std::ostream& operator<< ( std::ostream& out, Value& v ) {
        if( v.IsSignedInt() ) {
          out << v.m_SignedInt << "(sint)";
        }
        if( v.IsUnsignedInt() ) {
          out << v.m_UnsignedInt << "(uint)";
        }
        if( v.IsFloat() ) {
          out << v.m_Float << "(float)";
        }
        return out;
      }

    private:

      enum TypeT {
        tSignedInt,
        tUnsignedInt,
        tFloat
      };

      TypeT m_Type;

      union {
        signed   long long m_SignedInt;
        unsigned long long m_UnsignedInt;
        long double        m_Float;
      };

      template< typename T > inline void SetSignedInt( T v ) {
        m_Type = tSignedInt;
        m_SignedInt = v;
      }

      template< typename T > inline void SetUnsignedInt( T v ) {
        m_Type = tUnsignedInt;
        m_SignedInt = v;
      }

      template< typename T > inline void SetFloat( T v ) {
        m_Type = tFloat;
        m_Float = v;
      }
  };
  struct Operation {
    enum opCode {
      // Notyfi aboud the end of the script
      endScript = 0,

      // Push "current" address onto stack top modified with const argument
      pushCurrent,

      // load value from memory at address given at the stack top
      // and push the result back onto the stack
      loadMem,

      // get address from const argument,
      // read proper value from the memory and push it onto the stack
      // value is loaded from the code arguments array
      loadArg,

      // Simple arithmetic operations, pops two operands from stack top
      // and push the result onto the stack
      add,
      mul,
      div,
      mod,

      // Unary operators, pops operand from stack top and push the result
      neg,
      conv,

      // Call to function with one argument, arg popped from the stack, result pushed back
      fnSin,
      fnCos,
      fnTan,
      fnLn,

      // Call to function with two arguments
      fnPow,

    };

    // Argument modifiers
    enum modifier {
      modNone,
      modArg,
      modChar,
      modByte,
      modShort,
      modWord,
      modLong,
      modDword,
      modLongLong,
      modQword,
      modFloat,
      modDouble,
      modLongDouble,
    };
    unsigned m_OpCode: 8;
    unsigned m_Mod1:   4;
    unsigned m_Mod2:   4;
    short m_ConstArgument;
  };
  class Preprocessed {
    public:
      Preprocessed();
      ~Preprocessed();
      inline void Clear() {
        m_CodeArguments.clear();
        m_Code.clear();
      }
      inline int PushOperation( const Operation& op ) {
        m_Code.push_back( op );
        return ( int )m_Code.size() - 1;
      }
      inline int PushArgument( const Value& v ) {
        m_CodeArguments.push_back( v );
        return ( int )m_CodeArguments.size() - 1;
      }
      inline const Operation& GetOperation( int pos ) const {
        if( ( size_t )pos >= m_Code.size() ) {
          throw errorOperationIndex;
        }
        return m_Code[ pos ];
      }


      inline const Value& GetArgument( int pos ) const {
        if( ( size_t )pos >= m_CodeArguments.size() ) {
          throw errorArgumentIndex;
        }
        return m_CodeArguments[ pos ];
      }
      wxString DumpCode();
      wxString DumpArgs();
    private:
      std::vector< Value >     m_CodeArguments;       ///< \brief list of arguments
      std::vector< Operation > m_Code;                ///< \brief the code
  };
}

#endif
